package com.netty.modbus.coder;

import com.netty.modbus.constants.Constants;
import com.netty.modbus.dto.response.ReadResponseBody;
import com.netty.modbus.dto.response.WriteResponseBody;
import com.netty.modbus.util.CRC16;
import com.netty.modbus.util.HexUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * description: OneDecoder <br>
 * date: 2021/9/29 15:39 <br>
 * @author: lwy <br>
 * version: 1.0 <br>
 */
public class OneDecoder extends ByteToMessageDecoder {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
        // 数据转换
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.getBytes(byteBuf.readerIndex(), bytes);
        String hexId = HexUtil.byte2HexStr(bytes);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
        System.out.println(sdf.format(new Date())+"接收数据：{}"+ hexId);
        Object decoded = decode(ctx, byteBuf);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        // <---------------------  数据校验  -------------------->
        int length = in.readableBytes();
        if(length < Constants.NUM_7) {
            System.out.println("初始长度不够等待数据");
            return null;
        }
        // ByteBuf数据转换成byte数组
        byte[] bytes = new byte[length];
        in.getBytes(in.readerIndex(), bytes);
        // 数据长度
        int frameLength = frameLength(bytes);
        // 0 - 协议校验码有误
        if (frameLength == 0) {
            return in.readUnsignedByte();
        }
        // 数据校验
        int flag =  crc16(bytes, frameLength);
        // 1 - 长度不够等待数据包
        if (flag == 1) {
            System.out.println("解析后长度不够等待数据");
            return null;
        }
        // 0 - 校验错误丢掉一位继续解析
        if (flag == 0) {
            return in.readUnsignedByte();
        }
        // <---------------------  数据解析  -------------------->
        return in.readRetainedSlice(frameLength);
    }

    /**
     * 数据长度性校验
     *
     * 0 - 校验错误
     * 1 - 长度不够
     * 2 - 正常
     * @param bytes 数据
     * @return
     */
    private int frameLength(byte[] bytes) {
        int frameLength = 0;
        // 功能码
        String functionCode=HexUtil.oneByte2HexStr(bytes[2]);
        switch (functionCode) {
            case Constants.RtuFunction.CODE_1:
                frameLength = 131;
                break;
            case Constants.RtuFunction.CODE_2:
                frameLength = 7;
                break;
            case Constants.RtuFunction.CODE_3:
                frameLength = 137;
                break;
            case Constants.RtuFunction.CODE_4:
                frameLength = 397;
                break;
            case Constants.RtuFunction.CODE_5:
                if(bytes[4]==03){
                    ReadResponseBody readResponseBody=new ReadResponseBody(bytes);
                    frameLength = readResponseBody.getAllLength();
                }else{
                    frameLength=15;
                }
                break;
            default:
                System.out.println("客户端消息：协议功能码有误， 功能码：{}"+functionCode);
        }
        return frameLength;
    }


    /**
     * crc16校验
     *
     * @param bytes 校验目标数组
     * @param aduLength 校验长度
     * @return
     *      0 - 校验错误
     *      1 - 长度不够
     *      2 - 正常
     */
    public int crc16(byte[] bytes, int aduLength) {
        // 目标数据长度
        int targetLength = bytes.length;
        // 目标数据长度 小于 解析得到数据长度  等待后续数据
        if (targetLength < aduLength) {
            System.out.println("目标数据长度错误，目标数据长度小于解析得到数据长度，等待后续数据");
            return 1;
        }
        // 生成校验数据，需要去掉校验位两个字节和结束符两个字节
        int cdLength = aduLength - 6;
        byte[] cd = new byte[cdLength];
        if (cdLength >= 0) {
            System.arraycopy(bytes, 2, cd, 0, cdLength);
        }
        //校验数据
        String hexId = HexUtil.byte2HexStr(cd);
        System.out.println("校验数据："+hexId);
        // CRC校验码
        byte[] crc16 = CRC16.crc16(cd);
        // 请求的crc16校验码
        int target = bytes[cdLength+2] + bytes[cdLength + 3];
        // 生成的crc16校验码
        int produce = 0;
        for (byte p: crc16) {
            produce = produce + p;
        }
        // 相等校验通过
        if (target == produce) {
            return 2;
        }
        System.out.println("客户端消息：crc16校验出错");
        return 0;
    }
}
